package club.kynb.mall.product.service.impl;

import club.kynb.mall.product.api.IProductInfoService;
import club.kynb.mall.product.constant.SkuStatusEnum;
import club.kynb.mall.product.constant.SpuStatusEnum;
import club.kynb.mall.product.convert.ProductConvertor;
import club.kynb.mall.product.dto.ProductByParamDTO;
import club.kynb.mall.product.dto.ProductSkuDTO;
import club.kynb.mall.product.dto.ProductSpuDTO;
import club.kynb.mall.product.dto.ProductSpuDetailDTO;
import club.kynb.mall.product.mysql.mapper.ProductSkuMapper;
import club.kynb.mall.product.mysql.mapper.ProductSpuMapper;
import club.kynb.mall.product.mysql.po.ProductSkuPO;
import club.kynb.mall.product.mysql.po.ProductSpuPO;
import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.util.NumberUtil;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.pizza.common.web.exception.Errors;
import org.pizza.model.page.PageQuery;
import org.pizza.model.page.PageResult;
import org.pizza.mybatis.plus.support.Pages;
import org.pizza.util.Checker;
import org.springframework.stereotype.Service;

import java.util.Date;
import java.util.List;
import java.util.Objects;
import java.util.Optional;
import java.util.stream.Collectors;


/**
 * @author kynb_club@163.com
 * @date: 2021/6/24
 */
@Slf4j
@Service
@AllArgsConstructor
public class ProductInfoServiceImpl implements IProductInfoService {

    final ProductSpuMapper productSpuMapper;
    final ProductSkuMapper productSkuMapper;


    @Override
    public List<ProductSpuDetailDTO> listOnSaleByParam(ProductByParamDTO param) {
        List<ProductSpuDTO> productSpuDTOS = this.listSpuByParam(param.setStatusEnum(SpuStatusEnum.UP));
        return ProductConvertor.convert(productSpuDTOS,spu->spu2DetailDto(spu).orElse(null));
    }

    @Override
    public Optional<ProductSpuDetailDTO> detailOnSaleById(Long spuId) {
        ProductSpuPO spuPO = productSpuMapper.selectById(spuId);
        ProductSpuDTO spuDTO = ProductConvertor.convert(ProductSpuDTO.class, spuPO);
        return spu2DetailDto(spuDTO);
    }


    private Optional<ProductSpuDetailDTO> spu2DetailDto(ProductSpuDTO spuDTO) {
        if (null == spuDTO) {
            return Optional.empty();
        }
        ProductSpuDetailDTO detailDTO = ProductConvertor.convert(ProductSpuDetailDTO.class, spuDTO);
        List<ProductSkuDTO> skuList = listOnSaleSkuBySpuId(Long.valueOf(spuDTO.getId()));
        detailDTO.setSkuList(skuList);
        if(CollectionUtil.isEmpty(skuList)){
            return Optional.empty();
        }
        return Optional.of(detailDTO);
    }

    private List<ProductSkuDTO> listOnSaleSkuBySpuId(Long spuId) {
        List<ProductSkuDTO> skuDTOList = listSkuBySpuId(spuId).stream().filter(sku -> SkuStatusEnum.UP.getStatus().equals(sku.getSkuStatus())).collect(Collectors.toList());
        return ProductConvertor.convert(skuDTOList, ProductSkuDTO.class);
    }

    private LambdaQueryWrapper<ProductSpuPO> queryWrapper(ProductByParamDTO dto){
        LambdaQueryWrapper<ProductSpuPO> wrapper = Wrappers.<ProductSpuPO>lambdaQuery()
                .in(CollectionUtil.isNotEmpty(dto.getIds()), ProductSpuPO::getId, dto.getIds())
                .apply(Objects.nonNull(dto.getCateId()), "FIND_IN_SET ({0}, category_ids )", NumberUtil.toStr(dto.getCateId(), null))
                .and(StrUtil.isNotBlank(dto.getKeyword()),
                        w -> w.like(ProductSpuPO::getSpuName, dto.getKeyword())
                                .or().like(ProductSpuPO::getSpuKeywords, dto.getKeyword())
                )
                .orderByAsc(ProductSpuPO::getSequence);
        if (dto.getStatusEnum() != null) {
            switch (dto.getStatusEnum()) {
                case UP:
                    wrapper.eq(ProductSpuPO::getSpuOnSale, SpuStatusEnum.UP.getStatus())
                            .and(andW -> andW.isNull(ProductSpuPO::getOffSaleTime).or().gt(ProductSpuPO::getOffSaleTime, new Date()));
                    break;
                case INIT:
                    wrapper.eq(ProductSpuPO::getSpuOnSale, SpuStatusEnum.INIT.getStatus());
                    break;
                case DOWN:
                    wrapper.and(w -> w.eq(ProductSpuPO::getSpuOnSale, SpuStatusEnum.DOWN.getStatus())
                            .or(aw -> aw.isNotNull(ProductSpuPO::getOffSaleTime)
                                    .lt(ProductSpuPO::getOffSaleTime, new Date())));
                    break;
                default:
                    break;
            }
        }
        return wrapper;
    }

    @Override
    public List<ProductSpuDTO> listSpuByParam(ProductByParamDTO dto) {
        LambdaQueryWrapper<ProductSpuPO> wrapper = queryWrapper(dto);
        List<ProductSpuPO> spuPOList = productSpuMapper.selectList(wrapper);
        return ProductConvertor.convert(spuPOList, ProductSpuDTO.class);
    }

    @Override
    public void check(Long spuId, Long skuId) {
        final ProductSkuPO productSkuPO = productSkuMapper.selectById(skuId);
        Checker.ifNullThrow(productSkuPO, () -> Errors.BIZ.exception("skuId有误"));
        Checker.ifNotThrow(productSkuPO.getSpuId().equals(spuId), () -> Errors.BIZ.exception("spuId有误"));
    }



    /****************************  管理后台 ************************************/

    @Override
    public PageResult<ProductSpuDTO> pageQuery(ProductByParamDTO dto, PageQuery pageQuery) {
        IPage<ProductSpuPO> pageParam = Pages.convert(pageQuery);
        LambdaQueryWrapper<ProductSpuPO> queryWrapper = queryWrapper(dto);
        IPage<ProductSpuPO> page = productSpuMapper.selectPage(pageParam, queryWrapper);
        return Pages.convert(page, ProductSpuDTO.class);
    }

    @Override
    public void create(ProductSpuDTO spuDTO) {
        ProductSpuPO po = ProductConvertor.convert(ProductSpuPO.class, spuDTO);
        int result = productSpuMapper.insert(po);
        Checker.ifNotThrow(result > 0,()->Errors.BIZ.exception("商品创建失败"));
    }


    @Override
    public void update(ProductSpuDTO spuDTO) {
        ProductSpuPO po = ProductConvertor.convert(ProductSpuPO.class, spuDTO);
        int result = productSpuMapper.updateById(po);
        Checker.ifNotThrow(result > 0,()->Errors.BIZ.exception("商品更新失败"));
    }

    @Override
    public Optional<ProductSpuDTO> getSpuById(Long spuId) {
        ProductSpuPO spuPO = productSpuMapper.selectById(spuId);
        if(spuPO == null){
            return Optional.empty();
        }
        return Optional.of(ProductConvertor.convert(ProductSpuDTO.class, spuPO));
    }

    @Override
    public Optional<ProductSkuDTO> getSkuById(Long spuId) {
        ProductSkuPO skuPO = productSkuMapper.selectById(spuId);
        if(skuPO == null){
            return Optional.empty();
        }
        return Optional.of(ProductConvertor.convert(ProductSkuDTO.class, skuPO));
    }

    @Override
    public List<ProductSkuDTO> listSkuBySpuId(Long spuId) {
        List<ProductSkuPO> skuPos = productSkuMapper.selectList(
                Wrappers.<ProductSkuPO>lambdaQuery().eq(ProductSkuPO::getSpuId, spuId)
                        .orderByAsc(ProductSkuPO::getSequence));
        return ProductConvertor.convert(skuPos,ProductSkuDTO.class);
    }

    @Override
    public void create(ProductSkuDTO skuDTO) {
        ProductSkuPO po = ProductConvertor.convert(ProductSkuPO.class, skuDTO);
        int result = productSkuMapper.insert(po);
        Checker.ifNotThrow(result > 0,()->Errors.BIZ.exception("商品规格创建失败"));
    }

    @Override
    public void update(ProductSkuDTO skuDTO) {
        ProductSkuPO po = ProductConvertor.convert(ProductSkuPO.class, skuDTO);
        int result = productSkuMapper.updateById(po);
        Checker.ifNotThrow(result > 0,()->Errors.BIZ.exception("商品规格更新失败"));
    }


    @Override
    public Integer countOnSaleSkuBySpu(Long spuId) {
        return productSkuMapper.selectCount(
                Wrappers.<ProductSkuPO>lambdaQuery()
                        .eq(ProductSkuPO::getSpuId,spuId)
                        .eq(ProductSkuPO::getSkuStatus,SkuStatusEnum.UP.getStatus())
        );
    }
}
